FMTS=magproto.o gpx.o geo.o gpsman.o mapsend.o mapsource.o \
gpsutil.o tiger.o pcx.o csv.o cetus.o gpspilot.o magnav.o \
- psp.o mxf.o holux.o garmin.o ozi.o tmpro.o dna.o tpg.o gpsdrive.o
+ psp.o mxf.o holux.o garmin.o ozi.o tmpro.o dna.o tpg.o gpsdrive.o \
+ xcsv.o xmapwpt.o
JEEPS=jeeps/gpsapp.o jeeps/gpscom.o jeeps/gpsfmt.o jeeps/gpsinput.o \
jeeps/gpsmath.o jeeps/gpsmem.o \
mapsend.o: mapsend.c defs.h queue.h mapsend.h
mapsource.o: mapsource.c defs.h queue.h
mkshort.o: mkshort.c defs.h queue.h
+xmapwpt.o: mxf.c defs.h queue.h csv_util.h
+xcsv.o: xcsv.c defs.h queue.h csv_util.h
mxf.o: mxf.c defs.h queue.h csv_util.h
ozi.o: ozi.c defs.h queue.h csv_util.h
pcx.o: pcx.c defs.h queue.h
This is the format used to hot-sync to XMap from withing TopoUSA.
Done with help of Dan Edwards.
+ XMapWpt
+
+ Delorme XMapHandHeld Street Atlas USA is another of the billion
+ CSV variants. This is the format used by XmapHH SA USA on
+ (at least) PocketPC O/S. Please see README.xmapwpt for more
+ information on it's intricacies. This XMap is not to be confused
+ with the XMap mentioned above. Contributed to gpsbabel by
+ Alex Mottram.
+
+ XCSV
+
+ XCSV is an open-ended "Whatever Separated Values" parser / writer
+ designed to work with user-supplied "style" files. It should handle
+ at least a few thousand of the billion CSV variants available.
+ By itself, it doesn't comply to any format, however *most* CSV
+ variants can be described as a "style" and fine-tuned by the end
+ user. For more information on it's use, please see README.style
+ in the style/ sub-directory of gpsbabel. For an example of using
+ the XCSV module within your C program, look at the ozi.c, mxf.c, and
+ xmapwpt.c sources in the gpsbabel directory. This module was
+ contributed to gpsbabel by Alex Mottram.
+
+ Additional Options:
+ style - **REQUIRED** Path to XCSV style file.
+
+ snlen - Maximum length of synthesized shortnames.
+ snwhite - Switch defining whether or not to allow whitespace
+ in synthesized shortnames.
+ (0 = NO WHITESPACE, 1 = WHITESPACE OK).
+ snupper - Switch defining whether or not to force uppercase
+ in shortnames. (0 = LEAVE AS IS, 1 = UPPERCASE ALL).
+
+ NOTE: sn* options require use of the '-s' command line option.
+
+ Example Usage:
+ gpsbabel -i xsv,style=foo.style -f foo -o xsv,style=bar.style -F bar
+ gpsbabel -s -i gpx -f foo.gpx -o xsv,style=my.style,snlen=8 -F bar
+
MAPSEND
Magellan was smart enough to document their file format to make
--- /dev/null
+
+NOTE: THIS README PERTAINS TO THE "XMAPWPT" FORMAT, NOT THE XMAP/TOPO USA
+ 4.0 CONDUIT "XMAP" FORMAT.
+
+
+Delorme XMap Handheld .WPT for PocketPC is a bit of a kludge. This
+document covers XMap Handheld Street Atlas USA edition.
+
+XMap on the PocketPC stores it's waypoints in individual .wpt files.
+For example, waypoints generated by XMap on the PocketPC are stored
+by default in the "My Documents" folder using the sequential names
+"XMap1.wpt", "XMap2.wpt", ad nauseum. Needless to say, not very
+efficient.
+
+As writing multiple waypoint files is outside of the scope of gpsbabel,
+gpsbabel chooses to write one big file, one waypoint per line.
+Extracting lines from this file is left as an exercise for the end user.
+A simple perl script to handle this conversion is included at the end
+of this README.
+
+It should also be noted that READING multiple files is indeed possible,
+but if you have more than a few points, it can be a task. For example:
+
+gpsbabel -i xmapwpt -f Xmap1.wpt -f Xmap2.wpt -o mapsend -F mapsend.wpt
+
+will read the two Xmap .wpt files and write one mapsend file. This
+is fine for a small handful of points, but could be quite cumbersome
+for folks like me who have 100+ waypoints loaded into XMap. For *nix
+folks, something as simple as:
+
+cat *.wpt > /tmp/foo.wpt
+gpsbabel -i xmapwpt -f foo.wpt -o mapsend -F mapsend.wpt
+
+will do the trick just fine.
+
+
+############ BEGIN SCRIPT
+#!/full/path/to/perl
+$INPUTFILE = @ARGV[0];
+$TARGETDIR = @ARGV[1];
+$FILENAME = @ARGV[2];
+
+if (! $FILENAME) {
+ print "Usage: xmap_split.pl INPUT_FILE OUTPUT_DIRECTORY FILENAME_BASE\n";
+ print " (i.e. xmapl_split.pl points.wpt /tmp/points GPSB)\n";
+ print " (created GPSB0001-GPSBXXXX in /tmp/points/ from points.wpt)\n";
+ exit;
+}
+
+open (INFILE, $INPUTFILE) || die "Cannot open $INPUTFILE for read!\n";
+
+while (<INFILE>) {
+ $lc++;
+ $filename = sprintf("%s/Gpsb%04d.wpt", $TARGETDIR, $lc);
+
+ open (OUTFILE, ">$filename") || die "Cannot open $filename for write!\n";
+
+ print OUTFILE $_;
+
+ close(OUTFILE);
+}
+
+exit;
+
+########### END SCRIPT
+
/*
- Utilities for parsing Comma Seperated Value files (CSV)
+ Utilities for parsing Character Separated Value files (CSV)
Copyright (C) 2002 Alex Mottram (geo_alexm at cox-internet.com)
#define MYNAME "CSV_UTIL"
+/* macros */
+#define LAT_DIR(a) a < 0.0 ? 'S' : 'N'
+#define LON_DIR(a) a < 0.0 ? 'W' : 'E'
+
+/* convert excel time (days since 1900) to time_t and back again */
+#define EXCEL_TO_TIMET(a) ((a - 25569.0) * 86400.0)
+#define TIMET_TO_EXCEL(a) ((a / 86400.0) + 25569.0)
+
/*********************************************************************/
/* csv_stringclean() - remove any unwanted characters from string. */
/* returns copy of string. */
const char *sp;
static const char *p = NULL;
static char *tmp = NULL;
- size_t dlen, elen;
+ size_t dlen = 0, elen = 0;
int enclosedepth = 0;
short int dfound;
sp = p;
/* length of delimiters and enclosures */
- dlen = strlen(delimited_by);
- elen = strlen(enclosed_in);
+ if (delimited_by)
+ dlen = strlen(delimited_by);
+ if (enclosed_in)
+ elen = strlen(enclosed_in);
dfound = 0;
if ((!enclosedepth) && (strncmp(p, delimited_by, dlen) == 0)) {
dfound = 1;
-
} else {
p++;
}
return (tmp);
}
+/*****************************************************************************/
+/* dec_to_intdeg() - convert decimal degrees to integer degreees */
+/* usage: i = dec_to_intdeg(31.1234, 1); */
+/* i = dec_to_intdeg(91.1234, 0); */
+/*****************************************************************************/
+static int
+dec_to_intdeg(const double d, const int islat)
+{
+ int ideg = 0;
+
+ if (islat) {
+ ideg = (2147483647) - (d * 8388608);
+ } else {
+ ideg = (2147483647) - (fabs(d) * 8388608) + 1;
+ }
+
+ return(ideg);
+}
+
+/*****************************************************************************/
+/* intdeg_to_dec() - convert integer degrees to decimal degreees */
+/* usage: lat = dec_to_intdeg(ilat, 1); */
+/* lon = dec_to_intdeg(ilon, 0); */
+/*****************************************************************************/
+static double
+intdeg_to_dec(const int ideg, const int islat)
+{
+ double d;
+
+ if (islat) {
+ d = ((2147483647) - ideg) / (double)8388608;
+ } else {
+ d = ((-2147483647-1) + ideg) / (double)8388608;
+ }
+
+ return(d);
+}
+
+/*****************************************************************************/
+/* decdir_to_dec() - convert a decimal/direction value into pure decimal. */
+/* usage: lat = decdir_to_dec("W90.1234"); */
+/* lat = decdir_to_dec("30.1234N"); */
+/*****************************************************************************/
+static double
+decdir_to_dec(const char * decdir)
+{
+ char *p;
+ const char *cp;
+ double rval;
+ int sign = 0;
+
+ cp = &decdir[0];
+
+ if ((*cp == 'W') || (*cp == 'S'))
+ sign = -1;
+ else
+ if ((*cp == 'N') || (*cp == 'E'))
+ sign = 1;
+
+ rval = sign ? strtod(&decdir[1], &p) : strtod(&decdir[0], &p);
+
+ if (sign == 0) {
+ if ((*p == 'W') || (*p == 'S'))
+ sign = -1;
+ else
+ if ((*p == 'N') || (*p == 'E'))
+ sign = 1;
+ }
+
+ return(rval * sign);
+}
+
+/*****************************************************************************/
+/* xcsv_file_init() - prepare xcsv_file for first use. */
+/*****************************************************************************/
+void
+xcsv_file_init(void)
+{
+ memset(&xcsv_file, '\0', sizeof(xcsv_file_t));
+
+ QUEUE_INIT(&xcsv_file.prologue);
+ QUEUE_INIT(&xcsv_file.epilogue);
+
+ QUEUE_INIT(&xcsv_file.ifield);
+ /* ofield is alloced to allow pointing back at ifields
+ * where applicable.
+ */
+ xcsv_file.ofield = xcalloc(sizeof(queue), 1);
+ QUEUE_INIT(xcsv_file.ofield);
+}
+
+/*****************************************************************************/
+/* xcsv_ifield_add() - add input field to ifield queue. */
+/* usage: xcsv_ifield_add("DESCRIPTION", "", "%s") */
+/*****************************************************************************/
+void
+xcsv_ifield_add(char *key, char *val, char *pfc)
+{
+ field_map_t *fmp = xcalloc(sizeof(*fmp), 1);
+
+ fmp->key = key;
+ fmp->val = val;
+ fmp->printfc = pfc;
+
+ ENQUEUE_TAIL(&xcsv_file.ifield, &fmp->Q);
+ xcsv_file.ifield_ct++;
+}
+
+/*****************************************************************************/
+/* xcsv_ofield_add() - add output field to ofield queue. */
+/* usage: xcsv_ofield_add("LAT_DECIMAL", "", "%08.5lf") */
+/*****************************************************************************/
+void
+xcsv_ofield_add(char *key, char *val, char *pfc)
+{
+ field_map_t *fmp = xcalloc(sizeof(*fmp), 1);
+
+ fmp->key = key;
+ fmp->val = val;
+ fmp->printfc = pfc;
+
+ ENQUEUE_TAIL(xcsv_file.ofield, &fmp->Q);
+ xcsv_file.ofield_ct++;
+}
+
+/*****************************************************************************/
+/* xcsv_prologue_add() - add prologue line to prologue queue */
+/* usage: xcsv_prologue_add("Four score and seven years ago today,") */
+/*****************************************************************************/
+void
+xcsv_prologue_add(char *prologue)
+{
+ ogue_t* ogp = xcalloc(sizeof(*ogp), 1);
+
+ ogp->val = prologue;
+ ENQUEUE_TAIL(&xcsv_file.prologue, &ogp->Q);
+ xcsv_file.prologue_lines++;
+}
+
+/*****************************************************************************/
+/* xcsv_epilogue_add() - add epilogue line to epilogue queue */
+/* usage: xcsv_epilogue_add("shall not perish from the earth.") */
+/*****************************************************************************/
+void
+xcsv_epilogue_add(char *epilogue)
+{
+ ogue_t * ogp = xcalloc(sizeof(*ogp), 1);
+
+ ogp->val = epilogue;
+ ENQUEUE_TAIL(&xcsv_file.epilogue, &ogp->Q);
+ xcsv_file.epilogue_lines++;
+}
+
+/*****************************************************************************/
+/* xcsv_parse_val() - parse incoming data into the waypt structure. */
+/* usage: xcsv_parse_val("-123.34", *waypt, *field_map) */
+/*****************************************************************************/
+static void
+xcsv_parse_val(const char *s, waypoint *wpt, const field_map_t *fmp)
+{
+ if (strcmp(fmp->key, "IGNORE") == 0) {
+ /* IGNORE -- Categorically ignore this... */
+ } else
+ if (strcmp(fmp->key, "CONSTANT") == 0) {
+ /* CONSTANT -- Ignore on Input... */
+ } else
+ if (strcmp(fmp->key, "ANYNAME") == 0) {
+ /* ANYNAME -- Ignore -- this is output magic. */
+ } else
+ if (strcmp(fmp->key, "INDEX") == 0) {
+ /* IGNORE -- Calculated Sequence # For Ouput*/
+ } else
+ if (strcmp(fmp->key, "SHORTNAME") == 0) {
+ wpt->shortname = csv_stringtrim(s, "");
+ } else
+ if (strcmp(fmp->key, "DESCRIPTION") == 0) {
+ wpt->description = csv_stringtrim(s, "");
+ } else
+ if (strcmp(fmp->key, "NOTES") == 0) {
+ wpt->notes = csv_stringtrim(s, "");
+ } else
+ if (strcmp(fmp->key, "URL") == 0) {
+ wpt->url = csv_stringtrim(s, "");
+ } else
+ if (strcmp(fmp->key, "URL_LINK_TEXT") == 0) {
+ wpt->url_link_text = csv_stringtrim(s, "");
+ } else
+ if (strcmp(fmp->key, "ICON_DESCR") == 0) {
+ wpt->icon_descr = csv_stringtrim(s, "");
+ } else
+
+ /* LATITUDE CONVERSIONS**************************************************/
+ if (strcmp(fmp->key, "LAT_DECIMAL") == 0) {
+ /* latitude as a pure decimal value */
+ wpt->position.latitude.degrees = atof(s);
+ } else
+ if ((strcmp(fmp->key, "LAT_DECIMALDIR") == 0) ||
+ (strcmp(fmp->key, "LAT_DIRDECIMAL") == 0)) {
+ /* latitude as a decimal with N/S in it. */
+ wpt->position.latitude.degrees = decdir_to_dec(s);
+ } else
+ if (strcmp(fmp->key, "LAT_INT32DEG") == 0) {
+ /* latitude as a 32 bit integer offset */
+ wpt->position.latitude.degrees = intdeg_to_dec(atof(s), 1);
+ } else
+ /* LONGITUDE CONVERSIONS ***********************************************/
+ if (strcmp(fmp->key, "LON_DECIMAL") == 0) {
+ /* longitude as a pure decimal value */
+ wpt->position.longitude.degrees = atof(s);
+ } else
+ if ((strcmp(fmp->key, "LON_DECIMALDIR") == 0) ||
+ (strcmp(fmp->key, "LON_DIRDECIMAL") == 0)) {
+ /* longitude as a decimal with N/S in it. */
+ wpt->position.longitude.degrees = decdir_to_dec(s);
+ } else
+ if (strcmp(fmp->key, "LON_INT32DEG") == 0) {
+ /* longitude as a 32 bit integer offset */
+ wpt->position.longitude.degrees = intdeg_to_dec(atof(s), 0);
+ } else
+
+ /* DIRECTIONS **********************************************************/
+ if (strcmp(fmp->key, "LAT_DIR") == 0) {
+ /* latitude N/S. Ignore on input for now */
+ } else
+ if (strcmp(fmp->key, "LON_DIR") == 0) {
+ /* longitude E/W. Ingore on input for now */
+ } else
+
+ /* ALTITUDE CONVERSIONS ************************************************/
+ if (strcmp(fmp->key, "ALT_FEET") == 0) {
+ /* altitude in feet as a decimal value */
+ wpt->position.altitude.altitude_meters = atof(s) * .3048;
+ } else
+ if (strcmp(fmp->key, "ALT_METERS") == 0) {
+ /* altitude in meters as a decimal value */
+ wpt->position.altitude.altitude_meters = atof(s);
+ } else
+
+ /* TIME CONVERSIONS ***************************************************/
+ if (strcmp(fmp->key, "EXCEL_TIME") == 0) {
+ /* Time as Excel Time */
+ wpt->creation_time = EXCEL_TO_TIMET(atof(s));
+ } else
+ if (strcmp(fmp->key, "TIMET_TIME") == 0) {
+ /* Time as time_t */
+ wpt->creation_time = atol(s);
+ } else
+
+ /* GEOCACHING STUFF ***************************************************/
+ if (strcmp(fmp->key, "GEOCACHE_DIFF") == 0) {
+ /* Geocache Difficulty as an int */
+ wpt->gc_data.diff = atof(s) * 10;
+ } else
+ if (strcmp(fmp->key, "GEOCACHE_TERR") == 0) {
+ /* Geocache Terrain as an int */
+ wpt->gc_data.terr = atof(s) * 10;
+ } else
+ if (strcmp(fmp->key, "GEOCACHE_TYPE") == 0) {
+ /* Geocache Type - TODO */
+ /* wpt->gc_data.type = gs_mktype(s);*/
+
+ } else {
+ fprintf (stderr, "UNKNOWN STYLE DIRECTIVE: %s\n", fmp->key);
+ }
+}
+
+/*****************************************************************************/
+/* xcsv_data_read() - read input file, parsing lines, fields and handling */
+/* any data conversion (the input meat) */
+/*****************************************************************************/
+void
+xcsv_data_read(void)
+{
+ char buff[8192];
+ char *s;
+ waypoint *wpt_tmp;
+ int linecount = 0;
+ queue *elem, *tmp;
+ field_map_t *fmp;
+ ogue_t *ogp;
+
+ do {
+ linecount++;
+ memset(buff, '\0', sizeof(buff));
+ fgets(buff, sizeof(buff), xcsv_file.xcsvfp);
+
+ rtrim(buff);
+
+ /* skip over x many lines on the top for the prologue... */
+ if ((xcsv_file.prologue_lines) && ((linecount - 1) <
+ xcsv_file.prologue_lines)) {
+ continue;
+ }
+
+ /* We should skip over epilogue lines also. Since we don't want to
+ * pre-read the file to know how many data lines we should be seeing,
+ * we take this cheap shot at the data and cross our fingers.
+ */
+
+ QUEUE_FOR_EACH(&xcsv_file.epilogue, elem, tmp) {
+ ogp = (ogue_t *) elem;
+ if (strncmp(buff, ogp->val, strlen(ogp->val)) == 0) {
+ buff[0] = '\0';
+ break;
+ }
+ }
+
+ if (strlen(buff)) {
+ wpt_tmp = xcalloc(sizeof(*wpt_tmp), 1);
+
+ s = buff;
+ s = csv_lineparse(s, xcsv_file.field_delimiter, "", linecount);
+
+ /* reset the ifield queue */
+ elem = QUEUE_FIRST(&xcsv_file.ifield);
+
+ /* now rip the line apart, advancing the queue for each tear
+ * off the beginning of buff since there's no index into queue.
+ */
+ while (s) {
+ fmp = (field_map_t *) elem;
+ xcsv_parse_val(s, wpt_tmp, fmp);
+
+ elem = QUEUE_NEXT(elem);
+
+ if (elem == &xcsv_file.ifield) {
+ /* we've wrapped the queue. so stop parsing! */
+ break;
+ }
+
+ s = csv_lineparse(NULL, xcsv_file.field_delimiter, "",
+ linecount);
+ }
+ waypt_add(wpt_tmp);
+ }
+
+ } while (!feof(xcsv_file.xcsvfp));
+}
+
+/*****************************************************************************/
+/* xcsv_waypt_pr() - write output file, handling output conversions */
+/* (the output meat) */
+/*****************************************************************************/
+static void
+xcsv_waypt_pr(const waypoint *wpt)
+{
+ char buff[1024];
+ static int index = 0;
+ char *shortname = NULL;
+ char *description = NULL;
+ char * anyname = NULL;
+ int i;
+ field_map_t *fmp;
+ queue *elem, *tmp;
+
+ if (wpt->shortname) {
+ anyname = xstrdup(wpt->shortname);
+ } else
+ if (wpt->description) {
+ anyname = xstrdup(wpt->description);
+ } else
+ if (wpt->notes) {
+ anyname = xstrdup(wpt->notes);
+ } else
+ anyname = xstrdup("");
+
+ if ((anyname) && (global_opts.synthesize_shortnames)) {
+ anyname = mkshort(anyname);
+ }
+
+ if ((! wpt->shortname) || (global_opts.synthesize_shortnames)) {
+ if (wpt->description) {
+ if (global_opts.synthesize_shortnames)
+ shortname = mkshort(wpt->description);
+ else
+ shortname = csv_stringclean(wpt->description, xcsv_file.badchars);
+ } else {
+ /* no description available */
+ shortname = xstrdup("");
+ }
+ } else{
+ shortname = csv_stringclean(wpt->shortname, xcsv_file.badchars);
+ }
+
+ if (! wpt->description) {
+ if (shortname) {
+ description = csv_stringclean(shortname, xcsv_file.badchars);
+ } else {
+ description = xstrdup("");
+ }
+ } else {
+ description = csv_stringclean(wpt->description, xcsv_file.badchars);
+ }
+
+ i = 0;
+ QUEUE_FOR_EACH(xcsv_file.ofield, elem, tmp) {
+ fmp = (field_map_t *) elem;
+
+ if (i != 0)
+ fprintf (xcsv_file.xcsvfp, xcsv_file.field_delimiter);
+
+ i++;
+
+ if (strcmp(fmp->key, "IGNORE") == 0) {
+ /* IGNORE -- Write the char printf conversion */
+ snprintf (buff, sizeof(buff), fmp->printfc, "");
+ } else
+ if (strcmp(fmp->key, "INDEX") == 0) {
+ snprintf (buff, sizeof(buff), fmp->printfc, index + atoi(fmp->val));
+ } else
+ if (strcmp(fmp->key, "CONSTANT") == 0) {
+ snprintf (buff, sizeof(buff), fmp->printfc, fmp->val);
+ } else
+ if (strcmp(fmp->key, "SHORTNAME") == 0) {
+ snprintf (buff, sizeof(buff), fmp->printfc, shortname);
+ } else
+ if (strcmp(fmp->key, "ANYNAME") == 0) {
+ snprintf (buff, sizeof(buff), fmp->printfc, anyname);
+ } else
+ if (strcmp(fmp->key, "DESCRIPTION") == 0) {
+ snprintf (buff, sizeof(buff), fmp->printfc, description);
+ } else
+ if (strcmp(fmp->key, "NOTES") == 0) {
+ snprintf (buff, sizeof(buff), fmp->printfc, wpt->notes);
+ } else
+ if (strcmp(fmp->key, "URL") == 0) {
+ snprintf (buff, sizeof(buff), fmp->printfc, wpt->url);
+ } else
+ if (strcmp(fmp->key, "URL_LINK_TEXT") == 0) {
+ snprintf (buff, sizeof(buff), fmp->printfc, wpt->url_link_text);
+ } else
+ if (strcmp(fmp->key, "ICON_DESCR") == 0) {
+ snprintf (buff, sizeof(buff), fmp->printfc, wpt->icon_descr);
+ } else
+
+ /* LATITUDE CONVERSION***********************************************/
+ if (strcmp(fmp->key, "LAT_DECIMAL") == 0) {
+ /* latitude as a pure decimal value */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ wpt->position.latitude.degrees);
+ } else
+ if (strcmp(fmp->key, "LAT_DECIMALDIR") == 0) {
+ /* latitude as a decimal value with N/S after it */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ fabs(wpt->position.latitude.degrees),
+ LAT_DIR(wpt->position.latitude.degrees));
+ } else
+ if (strcmp(fmp->key, "LAT_DIRDECIMAL") == 0) {
+ /* latitude as a decimal value with N/S before it */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ LAT_DIR(wpt->position.latitude.degrees),
+ fabs(wpt->position.latitude.degrees));
+ } else
+ if (strcmp(fmp->key, "LAT_INT32DEG") == 0) {
+ /* latitude as an integer offset from 0 degrees */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ dec_to_intdeg(wpt->position.latitude.degrees, 1));
+ } else
+
+ /* LONGITUDE CONVERSIONS*********************************************/
+ if (strcmp(fmp->key, "LON_DECIMAL") == 0) {
+ /* longitude as a pure decimal value */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ wpt->position.longitude.degrees);
+ } else
+ if (strcmp(fmp->key, "LON_DECIMALDIR") == 0) {
+ /* latitude as a decimal value with N/S after it */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ fabs(wpt->position.longitude.degrees),
+ LON_DIR(wpt->position.longitude.degrees));
+ } else
+ if (strcmp(fmp->key, "LON_DIRDECIMAL") == 0) {
+ /* latitude as a decimal value with N/S before it */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ LON_DIR(wpt->position.longitude.degrees),
+ fabs(wpt->position.longitude.degrees));
+ } else
+ if (strcmp(fmp->key, "LON_INT32DEG") == 0) {
+ /* longitudee as an integer offset from 0 degrees */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ dec_to_intdeg(wpt->position.longitude.degrees, 0));
+ } else
+
+ /* DIRECTIONS *******************************************************/
+ if (strcmp(fmp->key, "LAT_DIR") == 0) {
+ /* latitude N/S as a char */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ LAT_DIR(wpt->position.latitude.degrees));
+ } else
+ if (strcmp(fmp->key, "LON_DIR") == 0) {
+ /* longitude E/W as a char */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ LON_DIR(wpt->position.longitude.degrees));
+ } else
+
+ /* ALTITUDE CONVERSIONS**********************************************/
+ if (strcmp(fmp->key, "ALT_FEET") == 0) {
+ /* altitude in feet as a decimal value */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ (wpt->position.altitude.altitude_meters * 3.2808));
+ } else
+ if (strcmp(fmp->key, "ALT_METERS") == 0) {
+ /* altitude in meters as a decimal value */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ wpt->position.altitude.altitude_meters);
+ } else
+
+ /* TIME CONVERSIONS**************************************************/
+ if (strcmp(fmp->key, "EXCEL_TIME") == 0) {
+ /* creation time as an excel (double) time */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ TIMET_TO_EXCEL(wpt->creation_time));
+ } else
+ if (strcmp(fmp->key, "TIMET_TIME") == 0) {
+ /* time as a time_t variable */
+ snprintf (buff, sizeof(buff), fmp->printfc, wpt->creation_time);
+ } else
+
+ /* GEOCACHE STUFF **************************************************/
+ if (strcmp(fmp->key, "GEOCACHE_DIFF") == 0) {
+ /* Geocache Difficulty as a double */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ wpt->gc_data.diff / 10);
+ } else
+ if (strcmp(fmp->key, "GEOCACHE_TERR") == 0) {
+ /* Geocache Terrain as a double */
+ snprintf (buff, sizeof(buff), fmp->printfc,
+ wpt->gc_data.terr / 10);
+ } else
+ if (strcmp(fmp->key, "GEOCACHE_TYPE") == 0) {
+ /* Geocache Type TODO */
+ snprintf (buff, sizeof(buff), fmp->printfc, "Geocache");
+ } else {
+ /* this should probably never happen */
+ }
+
+ fprintf (xcsv_file.xcsvfp, "%s", buff);
+
+ }
+
+ fprintf (xcsv_file.xcsvfp, "%s", xcsv_file.record_delimiter);
+
+ if (shortname)
+ free(shortname);
+
+ if (description)
+ free(description);
+
+ if (anyname)
+ free(anyname);
+
+ index++;
+}
+
+/*****************************************************************************/
+/* xcsv_data_write(void) - write prologues, spawn the output loop, and write */
+/* epilogues. */
+/*****************************************************************************/
+void
+xcsv_data_write(void)
+{
+ queue *elem, *tmp;
+ ogue_t *ogp;
+
+ /* output prologue lines, if any. */
+ QUEUE_FOR_EACH(&xcsv_file.prologue, elem, tmp) {
+ ogp = (ogue_t *) elem;
+ fprintf (xcsv_file.xcsvfp, "%s%s", ogp->val, xcsv_file.record_delimiter);
+ }
+
+ waypt_disp_all(xcsv_waypt_pr);
+
+ /* output epilogue lines, if any. */
+ QUEUE_FOR_EACH(&xcsv_file.epilogue, elem, tmp) {
+ ogp = (ogue_t *) elem;
+ fprintf (xcsv_file.xcsvfp, "%s%s", ogp->val, xcsv_file.record_delimiter);
+ }
+}
+
*/
/* function prototypes */
+
char *
csv_stringtrim(const char *string, const char *enclosure);
char *
csv_stringclean(const char *string, const char *chararray);
+
+void
+xcsv_data_read(void);
+
+void
+xcsv_data_write(void);
+
+void
+xcsv_file_init(void);
+
+void
+xcsv_prologue_add(char *);
+
+void
+xcsv_epilogue_add(char *);
+
+void
+xcsv_ifield_add(char *, char *, char *);
+
+void
+xcsv_ofield_add(char *, char *, char *);
+
+void
+xcsv_destroy_style(void);
+
+/****************************************************************************/
+/* types required for various xcsv functions */
+/****************************************************************************/
+
+/* something to map fields to waypts */
+typedef struct field_map {
+ queue Q;
+ char * key;
+ char * val;
+ char * printfc;
+} field_map_t;
+
+/* a queuing struct for prologues / epilogues */
+typedef struct ogue {
+ queue Q;
+ char * val;
+} ogue_t;
+
+/* something to map config file constants to chars */
+typedef struct char_map {
+ const char * key;
+ const char * chars;
+} char_map_t;
+
+/*
+ * a type describing all the wonderful elements of xcsv files, in a
+ * nutshell.
+ */
+typedef struct {
+ int is_internal; /* bool - is internal (1) or parsed (0) */
+
+ int prologue_lines; /* # of lines to ignore at top of the file */
+ int epilogue_lines; /* # of lines to ignore at bottom of file */
+
+ /* header lines for writing at the top of the file. */
+ queue prologue;
+
+ /* footer lines for writing at the bottom of the file. */
+ queue epilogue;
+
+ char * field_delimiter; /* comma, quote, etc... */
+ char * record_delimiter; /* newline, c/r, etc... */
+
+ char * badchars; /* characters we never write to output */
+
+ queue ifield; /* input field mapping */
+ queue * ofield; /* output field mapping */
+
+ int ifield_ct; /* actual # of ifields */
+ int ofield_ct; /* actual # of ofields */
+
+ FILE * xcsvfp; /* ptr to current *open* data file */
+
+} xcsv_file_t;
+
+
+/****************************************************************************/
+/* obligatory global struct */
+/****************************************************************************/
+xcsv_file_t xcsv_file;
void *xmalloc(size_t size);
char *xstrdup(const char *s);
void rtrim(char *s);
+signed int get_tz_offset(void);
/*
* PalmOS records like fixed-point numbers, which should be rounded
#include "defs.h"
#include "csv_util.h"
-#include <ctype.h>
#define MYNAME "MXF"
-static FILE *file_in;
-static FILE *file_out;
-
-static void
-rd_init(const char *fname, const char *args)
+static void
+mxf_set_style()
{
- file_in = fopen(fname, "r");
- if (file_in == NULL) {
- fatal(MYNAME ": Cannot open %s for reading\n", fname);
+ /* set up the csv xcsv_file struct */
+ xcsv_file_init();
+
+ /* this is an internal style, don't mess with it */
+ xcsv_file.is_internal = 1;
+
+ /* how the file gets split up */
+ xcsv_file.field_delimiter = ", ";
+ xcsv_file.record_delimiter = "\n";
+ xcsv_file.badchars = "\",";
+
+ xcsv_ifield_add("LAT_DECIMAL", "", "%08.5f");
+ xcsv_ifield_add("LON_DECIMAL", "", "%08.5f");
+ xcsv_ifield_add("DESCRIPTION", "", "%s");
+ xcsv_ifield_add("SHORTNAME", "", "%s");
+ xcsv_ifield_add("IGNORE", "", "%s");
+ xcsv_ifield_add("CONSTANT", "ff0000", "%s");
+ xcsv_ifield_add("CONSTANT", "47", "%s");
+
+ xcsv_ofield_add("LAT_DECIMAL", "", "%08.5f");
+ xcsv_ofield_add("LON_DECIMAL", "", "%08.5f");
+ xcsv_ofield_add("DESCRIPTION", "", "\"%s\"");
+ xcsv_ofield_add("SHORTNAME", "", "\"%s\"");
+ xcsv_ofield_add("DESCRIPTION", "", "\"%s\"");
+ xcsv_ofield_add("CONSTANT", "ff0000", "%s");
+ xcsv_ofield_add("CONSTANT", "47", "%s");
+
+ /* set up mkshort */
+ if (global_opts.synthesize_shortnames) {
+ setshort_length(32);
+ setshort_whitespace_ok(0);
+ setshort_badchars(xcsv_file.badchars);
}
}
-static void
-rd_deinit(void)
+static void
+mxf_rd_init(const char *fname, const char *args)
{
- fclose(file_in);
-}
+ mxf_set_style();
-static void
-wr_init(const char *fname, const char *args)
-{
- file_out = fopen(fname, "w");
- if (file_out == NULL) {
- fatal(MYNAME ": Cannot open %s for writing\n", fname);
+ xcsv_file.xcsvfp = fopen(fname, "r");
+
+ if (xcsv_file.xcsvfp == NULL) {
+ fatal(MYNAME ": Cannot open %s for reading\n", fname );
}
}
-static void
-wr_deinit(void)
+static void
+mxf_wr_init(const char *fname, const char *args)
{
- fclose(file_out);
-}
+ mxf_set_style();
-static void
-data_read(void)
-{
- char buff[1024];
- char *s;
- waypoint *wpt_tmp;
- int i;
- int linecount = 0;
-
- do {
- linecount++;
- memset(buff, '\0', sizeof(buff));
- fgets(buff, sizeof(buff), file_in);
-
- if (strlen(buff)) {
-
- wpt_tmp = xcalloc(sizeof(*wpt_tmp), 1);
-
- /* data delimited by commas, possibly enclosed in quotes. */
- s = buff;
-
- s = csv_lineparse(s, ",", "\"", linecount);
-
- i = 0;
- while (s) {
- switch (i) {
- case 0:
- wpt_tmp->position.latitude.degrees = atof(s);
- break;
- case 1:
- wpt_tmp->position.longitude.degrees = atof(s);
- break;
- case 2:
- wpt_tmp->description = csv_stringtrim(s, "");
- break;
- case 3:
- wpt_tmp->shortname = csv_stringtrim(s, "");
- break;
- case 4:
- /* ignore. another name-type */
- break;
- case 5:
- /* ignore: color */
- break;
- case 6:
- /* ignore: icon */
- break;
- default:
- /* whoa! nelly */
- fprintf (stderr, "%s: Warning: data fields on line %d exceed specification.\n",
- MYNAME, linecount);
- break;
- }
- i++;
-
- s = csv_lineparse(NULL, ",", "\"", linecount);
- }
-
- if (i != 7) {
- free(wpt_tmp);
- fprintf (stderr, "%s: WARNING - extracted %d fields from line %d. \nData on line ignored.\n",
- MYNAME, i, linecount);
- } else {
- waypt_add(wpt_tmp);
- }
-
- } else {
- /* empty line */
- }
-
- } while (!feof(file_in));
-}
-
-static void
-mxf_waypt_pr(const waypoint * wpt)
-{
- int icon = 47; /* default to "dot" */
- const char *color_hex = "ff0000";
- char *shortname = NULL;
- char *description = NULL;
-
- if ((! wpt->shortname) || (global_opts.synthesize_shortnames)) {
- if (wpt->description) {
- if (global_opts.synthesize_shortnames)
- shortname = mkshort(wpt->description);
- else
- shortname = csv_stringclean(wpt->description, ",\"");
- } else {
- /* no description available */
- shortname = xstrdup("");
- }
- } else{
- shortname = csv_stringclean(wpt->shortname, ",\"");
+ xcsv_file.xcsvfp = fopen(fname, "w");
+
+ if (xcsv_file.xcsvfp == NULL) {
+ fatal(MYNAME ": Cannot open %s for reading\n", fname );
}
-
- if (! wpt->description) {
- if (shortname) {
- description = csv_stringclean(shortname, ",\"");
- } else {
- description = xstrdup("");
- }
- } else{
- description = csv_stringclean(wpt->description, ",\"");
- }
-
- fprintf(file_out, "%08.5f, %08.5f, \"%s\", \"%s\", \"%s\", %s, %d\n",
- wpt->position.latitude.degrees, wpt->position.longitude.degrees,
- description, shortname, description,
- color_hex, icon);
-
- if (description)
- free(description);
- if (shortname)
- free(shortname);
}
-static void
-data_write(void)
+static void
+mxf_deinit(void)
{
- if (global_opts.synthesize_shortnames) {
- setshort_length(32);
- setshort_whitespace_ok(0);
- setshort_badchars("\",");
- }
-
- waypt_disp_all(mxf_waypt_pr);
+ if (xcsv_file.xcsvfp)
+ fclose(xcsv_file.xcsvfp);
+
+ xcsv_destroy_style();
}
ff_vecs_t mxf_vecs = {
- rd_init,
- wr_init,
- rd_deinit,
- wr_deinit,
- data_read,
- data_write,
+ mxf_rd_init,
+ mxf_wr_init,
+ mxf_deinit,
+ mxf_deinit,
+ xcsv_data_read,
+ xcsv_data_write,
};
-
#include "defs.h"
#include "csv_util.h"
-#include <ctype.h>
-#include <math.h> /* for floor */
#define MYNAME "OZI"
-static FILE *file_in;
-static FILE *file_out;
-
-static void
-rd_init(const char *fname, const char *args)
+static void
+ozi_set_style()
{
- file_in = fopen(fname, "r");
- if (file_in == NULL) {
- fatal(MYNAME ": Cannot open %s for reading\n", fname);
+ /* set up the ozi xcsv_file struct */
+ xcsv_file_init();
+
+ /* this is an internal style, don't mess with it */
+ xcsv_file.is_internal = 1;
+
+ /* how the file gets split up */
+ xcsv_file.field_delimiter = ",";
+ xcsv_file.record_delimiter = "\n";
+ xcsv_file.badchars = ",";
+
+ /* prologue */
+ xcsv_prologue_add("OziExplorer Waypoint File Version 1.1");
+ xcsv_prologue_add("WGS 84");
+ xcsv_prologue_add("Reserved 2");
+ xcsv_prologue_add("Reserved 3");
+
+ /* individual field mappings */
+ xcsv_ifield_add("INDEX", "1", "%4d");
+ xcsv_ifield_add("SHORTNAME", "", "%-14.14s");
+ xcsv_ifield_add("LAT_DECIMAL", "", "%11.6f");
+ xcsv_ifield_add("LON_DECIMAL", "", "%11.6f");
+ xcsv_ifield_add("EXCEL_TIME", "", "%011.5f");
+ xcsv_ifield_add("CONSTANT", "0", "%3s"); /* icon */
+ xcsv_ifield_add("CONSTANT", "1", "%2s"); /* 1 */
+ xcsv_ifield_add("CONSTANT", "3", "%2s"); /* display format opts */
+ xcsv_ifield_add("CONSTANT", "0", "%10s"); /* foreground color */
+ xcsv_ifield_add("CONSTANT", "65535", "%10s"); /* background color */
+ xcsv_ifield_add("DESCRIPTION", "", "%-40.40s");
+ xcsv_ifield_add("CONSTANT", "0", "%2s"); /* pointer direction */
+ xcsv_ifield_add("CONSTANT", "0", "%2s"); /* garmin display flags */
+ xcsv_ifield_add("CONSTANT", "0", "%5s"); /* proximity distance */
+ xcsv_ifield_add("ALT_FEET", "", "%7.0f");
+ xcsv_ifield_add("CONSTANT", "6", "%2s"); /* waypt name text size */
+ xcsv_ifield_add("CONSTANT", "0", "%2s"); /* bold checkbox */
+ xcsv_ifield_add("CONSTANT", "17", "%2s"); /* symbol size */
+
+ /* outfields are infields */
+ if (xcsv_file.ofield)
+ free(xcsv_file.ofield);
+ xcsv_file.ofield = &xcsv_file.ifield;
+ xcsv_file.ofield_ct = xcsv_file.ifield_ct;
+
+ /* set up mkshort */
+ if (global_opts.synthesize_shortnames) {
+ setshort_length(32);
+ setshort_whitespace_ok(0);
+ setshort_badchars(xcsv_file.badchars);
}
}
-static void
-rd_deinit(void)
-{
- fclose(file_in);
-}
-
-static void
-wr_init(const char *fname, const char *args)
+static void
+ozi_rd_init(const char *fname, const char *args)
{
- file_out = fopen(fname, "w");
- if (file_out == NULL) {
- fatal(MYNAME ": Cannot open %s for writing\n", fname);
+ ozi_set_style();
+
+ xcsv_file.xcsvfp = fopen(fname, "r");
+
+ if (xcsv_file.xcsvfp == NULL) {
+ fatal(MYNAME ": Cannot open %s for reading\n", fname);
}
}
-static void
-wr_deinit(void)
-{
- fclose(file_out);
-}
-
-static void
-data_read(void)
-{
- char buff[1024];
- char *s;
- waypoint *wpt_tmp;
- int i;
- int linecount = 0;
- double alt;
-
- do {
- linecount++;
- memset(buff, '\0', sizeof(buff));
- fgets(buff, sizeof(buff), file_in);
-
- if ((strlen(buff)) && (strstr(buff, ",") != NULL)) {
-
- wpt_tmp = xcalloc(sizeof(*wpt_tmp), 1);
-
- /* data delimited by commas, possibly enclosed in quotes. */
- s = buff;
- s = csv_lineparse(s, ",", "", linecount);
-
- i = 0;
- while (s) {
- switch (i) {
- case 0:
- /* sequence # */
- break;
- case 1:
- /* waypoint name */
- wpt_tmp->shortname = csv_stringtrim(s, "");
- break;
- case 2:
- /* degrees latitude */
- wpt_tmp->position.latitude.degrees = atof(s);
- break;
- case 3:
- /* degrees longitude */
- wpt_tmp->position.longitude.degrees = atof(s);
- break;
- case 4:
- /* DAYS since 1900 00:00:00 in days.days (5.5) */
- wpt_tmp->creation_time = (atof(s) - 25569.0) * 86400.0;
- break;
- case 5:
- /* icons 0-xx */
- break;
- case 6:
- /* unknown - always 1 */
- break;
- case 7:
- /* display format options 0-8 */
- break;
- case 8:
- /* foreground color (0=black) */
- break;
- case 9:
- /* background color (65535=yellow)*/
- break;
- case 10:
- /* Description */
- wpt_tmp->description = csv_stringtrim(s, "");
-
- break;
- case 11:
- /* pointer direction 0,1,2,3 bottom,top,left,right */
- break;
- case 12:
- /* garmin gps display flags (0-name w/sym, 1-sym only, 2-comment w/symbol */
- break;
- case 13:
- /* proximity distance - meters */
- break;
- case 14:
- /* altitude in feet */
- alt = atof(s);
- if (alt == -777) {
- wpt_tmp->position.altitude.altitude_meters = unknown_alt;
- } else {
- wpt_tmp->position.altitude.altitude_meters = alt * .3048;
- }
- break;
- case 15:
- /* waypoint text name size */
- break;
- case 16:
- /* bold checkbox (1=bold, default 0) */
- break;
- case 17:
- /* symbol size - 17 default */
- break;
- /*
- * Fields 18-23 were added around version 3.90.4g of
- * Ozi, but aren't documented. We silently ignore
- * these or any additional fields we don't need.
- */
- default:
- break;
- }
- i++;
-
- s = csv_lineparse(NULL, ",", "", linecount);
- }
-
- waypt_add(wpt_tmp);
-
- } else {
- /* empty line */
- }
-
- } while (!feof(file_in));
-}
-
-static void
-ozi_waypt_pr(const waypoint * wpt)
+static void
+ozi_wr_init(const char *fname, const char *args)
{
- static int index = 0;
- double alt_feet;
- double ozi_time;
- char * description;
- char * shortname;
+ ozi_set_style();
- ozi_time = (wpt->creation_time / 86400.0) + 25569.0;
- if (wpt->position.altitude.altitude_meters == unknown_alt) {
- alt_feet = -777;
- } else {
- alt_feet = (wpt->position.altitude.altitude_meters * 3.2808);
- }
+ xcsv_file.xcsvfp = fopen(fname, "w");
- if ((! wpt->shortname) || (global_opts.synthesize_shortnames)) {
- if (wpt->description) {
- if (global_opts.synthesize_shortnames)
- shortname = mkshort(wpt->description);
- else
- shortname = csv_stringclean(wpt->description, ",");
- } else {
- /* no description available */
- shortname = xstrdup("");
- }
- } else{
- shortname = csv_stringclean(wpt->shortname, ",");
- }
-
- if (! wpt->description) {
- if (shortname) {
- description = csv_stringclean(shortname, ",");
- } else {
- description = xstrdup("");
- }
- } else{
- description = csv_stringclean(wpt->description, ",");
+ if (xcsv_file.xcsvfp == NULL) {
+ fatal(MYNAME ": Cannot open %s for reading\n", fname);
}
-
- index++;
-
- fprintf(file_out, "%4d,%-14.14s,%11.6f,%11.6f,%011.5f,%3d,%2d,%2d,%10d,%10d,%-40.40s,%2d,%2d,%5d,%7.0f,%2d,%2d,%2d\n",
- index, shortname, wpt->position.latitude.degrees,
- wpt->position.longitude.degrees, ozi_time, 0, 1, 3, 0, 65535,
- description, 0, 0, 0, alt_feet, 6, 0, 17);
-
- free(description);
- free(shortname);
-
}
-static void
-data_write(void)
+static void
+ozi_deinit(void)
{
- fprintf(file_out, "OziExplorer Waypoint File Version 1.1\n");
- fprintf(file_out, "WGS 84\n");
- fprintf(file_out, "Reserved 2\n");
- fprintf(file_out, "Reserved 3\n");
-
- if (global_opts.synthesize_shortnames) {
- setshort_length(32);
- setshort_whitespace_ok(0);
- setshort_badchars("\",");
- }
-
- waypt_disp_all(ozi_waypt_pr);
+ if (xcsv_file.xcsvfp)
+ fclose(xcsv_file.xcsvfp);
+
+ xcsv_destroy_style();
}
ff_vecs_t ozi_vecs = {
- rd_init,
- wr_init,
- rd_deinit,
- wr_deinit,
- data_read,
- data_write,
+ ozi_rd_init,
+ ozi_wr_init,
+ ozi_deinit,
+ ozi_deinit,
+ xcsv_data_read,
+ xcsv_data_write,
};
-
--- /dev/null
+1296126539:1481466224:1845728360:1416544806:3137157:GCEBB::Mountain Bike Heaven by susy1313
+1296126539:1481466224:1844733052:1420362881:3137157:GC1A37::The Troll by a182pilot & Family
+1296126539:1481466224:1845525076:1420861444:3137157:GC1C2B::Dive Bomber by JoGPS & family
+1296126539:1481466224:1845170937:1420622369:3137157:GC25A9::FOSTER by JoGPS & Family
+1296126539:1481466224:1844552696:1419840970:3137157:GC2723::Logan Lighthouse by JoGps & Family
+1296126539:1481466224:1844956189:1419432025:3137157:GC2B71::Ganier Cache by Susy1313
+1296126539:1481466224:1844757518:1419270824:3137157:GC309F::Shy's Hill by FireFighterEng33
+1296126539:1481466224:1845011414:1418580721:3137157:GC317A::GittyUp by JoGPS / Warner Parks
+1296126539:1481466224:1844799182:1418788060:3137157:GC317D::Inlighting by JoGPS / Warner Parks
--- /dev/null
+gpsbabel XCSV Style File Layout:
+
+The format of an XCSV style file is quite simple and designed to be easily
+implemented by non-programmers to handle "one-off" babel-ization of various
+XCSV (whatever separated values) text files. The format and usage of the
+various style directives are described below.
+
+The first and foremost important step is understanding how the config
+file is laid out itself. The format is:
+
+DIRECTIVE<whitespace>VALUE
+
+Where <whitespace> is a space, tab, spaces, tabs, etc... There should
+be *nothing* before the directive. (i.e. not " DIRECTIVE VALUE")
+
+INTERNAL CONSTANTS:
+A few internal constants are defined in the XCSV parser to make the style
+file simpler. They may or may be used and are optional in most cases.
+Note that only certain style file directives map these constants.
+
+STYLE CONSTANT MAPS TO CHAR(s)
+---------------------------------------
+COMMA ,
+COMMASPACE ,<space>
+SINGLEQUOTE '
+DOUBLEQUOTE "
+COLON :
+SEMICOLON ;
+NEWLINE \n
+CR \r
+CRNEWLINE \r\n
+TAB \t
+SPACE <space>
+HASH #
+
+COMMENTS:
+Anything after a hash (#) on a line is not parsed. For example:
+#THIS ENTIRE LINE IS A COMMENT.
+#FIELD LAT_DECIMAL, "", "%lf" THIS ENTIRE LINE IS A COMMENT
+FIELD LAT_DECIMAL, "", "%lf" # ONLY THIS SENTENCE IS A COMMENT.
+
+
+DEFINING THE LAYOUT OF THE FILE:
+--------------------------------
+The first few directives define the layout the physical file itself:
+
+ o FIELD_DELIMITER:
+ The field delimiter defines the character(s) that separate the fields in
+ the rows of data inside the XCSV file. Common field delimiters are commas
+ and tabs. (referred to as "comma separated values" and "tab separated
+ values")
+
+ examples: FIELD_DELIMITER COMMA
+ FIELD_DELIMITER ~
+
+ The directive FIELD_DELIMITER is parsed for STYLE CONSTANTS as defined in
+ the table above.
+
+ o RECORD DELIMITER:
+ The record delimiter defines that character(s) that separate ROWS of
+ data (FIELDS) in the XCSV file. The most common record delimiters
+ are NEWLINE and CR (carriage return).
+
+ example: RECORD_DELIMITER NEWLINE
+ RECORD_DELIMITER |
+
+ The directive RECORD_DELIMITER is parsed for STYLE CONSTANTS as defined
+ in the table above.
+
+ o BADCHARS:
+ Bad characters are things that should *never* be written into the XCSV
+ file on output. Common bad characters are usually the FIELD_DELIMITER
+ itself.
+
+ example: BADCHARS COMMA
+ BADCHARS ~|
+
+ The directive BADCHARS is parsed for STYLE CONSTANTS as defined in the
+ table above.
+
+ o PROLOGUE
+ A prologue is basically constant data that is written to the output
+ file BEFORE any waypoints are processed. PROLOGUE can be defined
+ multiple times in the style file, once for each "line" before the data
+ begins. This is commonly used in XCSV files as a "header" row.
+
+ example: PROLOGUE OziExplorer Waypoint File Version 1.1
+ PROLOGUE WGS 84
+ * or *
+ PROLOGUE Symbol,Name,Latitude,Longitude
+
+ o EPILOGUE
+ An Epilogue is the same as a prologue, except this data is written at
+ the END of the file. See the examples for PROLOGUE above.
+
+
+DEFINING FIELDS WITHIN THE FILE:
+-------------------------------
+
+A field defines data. There are two different classifications of FIELDS,
+IFIELD (file input) and OFIELD (file output). In the absence of any OFIELDS,
+IFIELDS are use as both input and output. The existence of OFIELDS is
+primarily to allow more flexible mapping of gpsbabel data to output data
+(say, for instance, to map the internal gpsbabel "description" variable to
+two or more fields on output). For all practical purposes, IFIELDS and
+OFIELDS are defined the same way in the style file.
+
+There are several different types of fields that may be defined. Each field
+consists of three pieces of information: the FIELD TYPE, a DEFAULT VALUE, and
+a PRINTF CONVERSION (for output). In many cases, not all pieces are used,
+but all 3 pieces are required.
+
+FIELDS should be defined in the style file in the logical order that they
+appear in the data, from left to right. This is the order in which they are
+parsed from input and written to output.
+
+The fields used by the XCSV parser are as follows:
+
+ o IGNORE
+ IGNORE fields are, guess what, ignored on input. Internally, IGNORE
+ fields are treated as CHARACTER data, and as such, require a printf
+ conversion for a character array.
+
+ example: IFIELD IGNORE,"","%14.14s" (writes a 14 character blank field)
+ IFIELD IGNORE,"","%s" (writes a blank field on output)
+
+ o CONSTANT
+ CONSTANT fields are, of course, constant. They are ignored on input,
+ however they write CONSTANT data on output. As such, they require a
+ DEFAULT VALUE and a printf conversion for a character array.
+
+ example: IFIELD CONSTANT,"FFFFFF","%s" (writes "FFFFFF" in the field)
+ IFIELD CONSTANT,"01/01/70","%s" (a constant date field)
+
+ o INDEX
+ An INDEX field is used ONLY on output. The INDEX constant defines a field
+ that, at output, contains the sequence number of the waypoint being
+ written, starting at 0. An index is managed internally as an INTEGER
+ and requires an INTEGER printf conversion. An INDEX has one special
+ property. The DEFAULT VALUE of the index is added to the index
+ on each iteration (to allow indexes starting at 1, 100, etc..).
+
+ example: IFIELD INDEX,"0","%04d" (Starts counting at zero)
+ IFIELD INDEX,"","%04d" (Starts counting at zero)
+ IFIELD INDEX,"1","%04d" (Starts counting at one)
+
+ o SHORTNAME
+ A SHORTNAME is generally the waypoint name of the data being processed.
+ SHORTNAME maps directly to the gpsbabel variable ->shortname. A SHORTNAME
+ is CHARACTER data and requires a character array printf conversion.
+
+ example: IFIELD SHORTNAME,"","%s" (write shortname in the output file)
+
+ o DESCRIPTION
+ A DESCRIPTION is generally a long description of the waypoint. A
+ DESCRIPTION maps to the gpsbabel variable ->description and is otherwise
+ handled exactly like a SHORTNAME.
+
+ example: IFIELD DESCRIPTION,"","%s" (write description in the output file)
+
+ o NOTES
+ NOTES are generally everything else about a waypoints. NOTES map to the
+ gpsbabel variable ->notes and is otherwise handled exactly like a
+ SHORTNAME.
+
+ o URL
+ URL is a URL for the waypoint. URL maps to the gpsbabel variable
+ ->url and is otherwise handled exactly like a SHORTNAME.
+
+ example: IFIELD URL,"","%s" (writes the URL in the output file)
+
+ o URL_LINK_TEXT
+ URL_LINK_TEXT is a textual description of where a URL points.
+ URL_LINK_TEXT maps to the gpsbabel variable ->url_link_text and
+ is otherwise handled exactly like a SHORTNAME.
+
+ example: IFIELD URL_LINK_TEXT,"","%s" (writes link text in the output file)
+
+ o ICON_DESCR
+ ICON_DESCR is a textual description of an icon type for a waypoint.
+ ICON_DESCR maps to the gpsbabel variable ->icon_desc and is otherwise
+ handled exactly like a SHORTNAME.
+
+ example: IFIELD ICON_DESCR,"","%s" (writes link text in the output file)
+
+ o LAT_DECIMAL
+ LAT_DECIMAL defines LATITUDE in DECIMAL format. Note that this is a PURE
+ signed decimal format (i.e. -91.0000). This data is handled internally as
+ a DOUBLE PRECISION FLOAT and requires a FLOATING POINT printf conversion.
+
+ example: IFIELD LAT_DECIMAL,"","%lf"
+
+ o LON_DECIMAL
+ See LAT_DECIMAL, except LON_DECIMAL defines LONGITUDE.
+
+ o LAT_INT32DEG
+ LAT_INT32DEG defines LATITUDE in what I call INT32DEGREES. This value is
+ a signed LONG INTEGER and requires a LONG INTEGER printf conversion.
+
+ example: IFIELD LAT_INT32DEG,"","%ld"
+
+ o LON_INT32DEG
+ See LON_INT32DEG except LON_INT32DEG defines LONGITUDE.
+
+ o LAT_DECIMALDIR / LAT_DIRDECIMAL
+ LAT_DECIMALDIR and LAT_DIRDECIMAL defines LATITUDE in DECIMAL format
+ with the added bonus of a 'N/S' or 'E/W' direction character. This data
+ is handled internally as a DOUBLE PRECISION FLOAT and a single
+ CHARACTER and requires a FLOATING POINT as well as a CHARACTER printf
+ conversion. The only difference between the two is whether the directional
+ character appears before (LAT_DIRDECIMAL) or after (LAT_DECIMALDIR) the
+ decimal number.
+
+ example: IFIELD LAT_DECIMALDIR,"","%lf %c"
+ example: IFIELD LAT_DIRDECIMAL,"","%c %08.5lf"
+
+ o LON_DECIMALDIR / LON_DIRDECIMAL
+ Same as LAT_DECIMALDIR / LAT_DIRDECIMAL except LON_ defines LONGITUDE.
+
+
+ o ALT_FEET
+ ALT_FEET is the position's ALTITUDE in FEET. This value is treated as
+ a SIGNED DOUBLE PRECISION FLOAT and requires a FLOATING POINT printf
+ conversion.
+
+ example: IFIELD ALT_FEET,"","%.0lf"
+
+ o ALT_METERS
+ ALT_METERS is identical to ALT_FEET with the exception that the altitude
+ is in METERS.
+
+ o EXCEL_TIME
+ EXCEL_TIME is the waypoint's creation time, if any. This is actually
+ the decimal days since 1/1/1900 and is handled internally as a DOUBLE
+ PRECISION FLOAT and requires a FLOATING POINT printf conversion.
+
+ example: IFIELD EXCEL_TIME,"","%11.5lf"
+
+ o TIMET_TIME
+ TIMET_TIME is the waypoint's creation time, if any. This is actually
+ the integer seconds since 1/1/1970 (let's not start the holy war) and
+ is handled internally as a LONG INTEGER and requires a LONG INTEGER
+ printf conversion.
+
+ example: IFIELD TIMET_TIME,"","%ld"
+
+
+EXAMPLES:
+--------
+For examples on using the XCSV module, please see the *.style files in
+the style/ subdirectory of gpsbabel. For examples of using the XCSV
+module instead of carving out trivial C code, see the source code
+examples ozi.c, mxf.c, and xmapwpt.c in the gpsbabel directory.
+
--- /dev/null
+# gpsbabel XCSV style file
+#
+# Format: Delorme SA 9.0 CSV
+# Author: Alex Mottram
+# Date: 12/09/2002
+#
+#
+# As defined in csv.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER COMMASPACE
+RECORD_DELIMITER NEWLINE
+BADCHARS COMMA
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD LAT_DECIMAL, "", "%08.5lf"
+IFIELD LON_DECIMAL, "", "%08.5lf"
+IFIELD DESCRIPTION, "", "%s"
--- /dev/null
+# gpsbabel XCSV style file
+#
+# Format: Custom "Everything" Style
+# Author: Alex Mottram
+# Date: 11/24/2002
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER COMMA
+RECORD_DELIMITER NEWLINE
+BADCHARS COMMA
+
+#
+# HEADER STUFF:
+#
+PROLOGUE Prologue Line 1
+PROLOGUE Prologue Line 2
+
+#
+# INDIVIDUAL DATA FIELDS:
+#
+IFIELD CONSTANT, "", "CONSTANT"
+IFIELD INDEX, "", "%d"
+IFIELD LAT_DECIMAL, "", "%lf"
+IFIELD LAT_DIR, "", "%c"
+IFIELD LON_DECIMAL, "", "%lf"
+IFIELD LON_DIR, "", "%c"
+IFIELD ICON_DESCR, "", "%s"
+IFIELD SHORTNAME, "", "%s"
+IFIELD DESCRIPTION, "", "%s"
+IFIELD NOTES, "", "%s"
+IFIELD URL, "", "%s"
+IFIELD URL_LINK_TEXT, "", "%s"
+IFIELD ALT_METERS, "", "%lfM"
+IFIELD ALT_FEET, "", "%lfF"
+IFIELD LAT_DECIMALDIR, "", "%lf/%c"
+IFIELD LON_DECIMALDIR, "", "%lf/%c"
+IFIELD LAT_DIRDECIMAL, "", "%c/%lf"
+IFIELD LON_DIRDECIMAL, "", "%c/%lf"
+IFIELD LAT_INT32DEG, "", "%ld"
+IFIELD LON_INT32DEG, "", "%ld"
+IFIELD TIMET_TIME, "", "%ld"
+IFIELD EXCEL_TIME, "", "%lf"
+
+# EPILOGUE:
+EPILOGUE Epilogue Line 1
+EPILOGUE Epilogue Line 2
--- /dev/null
+# gpsbabel XCSV style file
+#
+# Format: DNA Marker Format
+# Author: Alex Mottram
+# Date: 12/09/2002
+#
+#
+# As defined in dna.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER COMMA
+RECORD_DELIMITER NEWLINE
+BADCHARS COMMA
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD INDEX, "", "%d"
+IFIELD LAT_DECIMAL, "", "%08.5lf"
+IFIELD LON_DECIMAL, "", "%08.5lf"
+IFIELD DESCRIPTION, "", "%s"
+
--- /dev/null
+# gpsbabel XCSV style file
+#
+# Format: GPSDrive
+# Author: Alex Mottram
+# Date: 12/11/2002
+#
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER SPACE
+RECORD_DELIMITER NEWLINE
+BADCHARS ,"
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+
+IFIELD ANYNAME, "", "%s"
+IFIELD LAT_DECIMAL, "", "%08.5lf"
+IFIELD LON_DECIMAL, "", "%08.5lf"
--- /dev/null
+# gpsbabel XCSV style file
+#
+# Format: GPSMAN Format
+# Author: Alex Mottram
+# Date: 12/09/2002
+#
+#
+# As defined in gpsman.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER TAB
+RECORD_DELIMITER NEWLINE
+BADCHARS TAB
+
+PROLOGUE !Format: DDD 1 WGS 84
+PROLOGUE !W:
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD SHORTNAME, "", "%-8.8s"
+IFIELD DESCRIPTION, "", "%s"
+IFIELD LAT_DIRDECIMAL, "", "%c%lf"
+IFIELD LON_DIRDECIMAL, "", "%c%lf"
+IFIELD IGNORE, "", "%s"
+
+# gpsman.c likes mkshort len = 8, whitespace = 0.
--- /dev/null
+# gpsbabel XCSV style file
+#
+# Format: Ozi Explorer
+# Author: Alex Mottram
+# Date: 12/09/2002
+#
+#
+# As used in mxf.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER COMMASPACE
+RECORD_DELIMITER NEWLINE
+BADCHARS COMMA
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD LAT_DECIMAL, "", "%08.5f"
+IFIELD LON_DECIMAL, "", "%08.5f"
+IFIELD DESCRIPTION, "", "%s"
+IFIELD SHORTNAME, "", "%s"
+IFIELD IGNORE, "", "%s"
+IFIELD CONSTANT, "ff0000", "%s" # COLOR
+IFIELD CONSTANT, "47", "%s" # ICON
+
+OFIELD LAT_DECIMAL, "", "%08.5f"
+OFIELD LON_DECIMAL, "", "%08.5f"
+OFIELD DESCRIPTION, "", ""%s""
+OFIELD SHORTNAME, "", "%s"
+OFIELD DESCRIPTION, "", "%s"
+OFIELD CONSTANT, "ff0000", "%s" # COLOR
+OFIELD CONSTANT, "47", "%s" # ICON
--- /dev/null
+# gpsbabel XCSV style file
+#
+# Format: NIMA/GNIS Geographic Names File
+# Author: Alex Mottram
+# Date: 11/24/2002
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER TAB
+RECORD_DELIMITER NEWLINE
+BADCHARS TAB
+PROLOGUE RC UFI UNI DD_LAT DD_LONG DMS_LAT DMS_LONG UTM JOG FC DSG PC CC1 ADM1 ADM2 DIM CC2 NT LC SHORT_FORM GENERIC SORT_NAME FULL_NAME FULL_NAME_ND MODIFY_DATE
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD IGNORE, "", "%s" # RC
+IFIELD IGNORE, "", "%s" # UFI
+IFIELD IGNORE, "", "%s" # UNI
+IFIELD LAT_DECIMAL, "", "%lf" # DD_LAT
+IFIELD LON_DECIMAL, "", "%lf" # DD_LON
+IFIELD IGNORE, "", "%s" # DMS_LAT
+IFIELD IGNORE, "", "%s" # DMS_LON
+IFIELD IGNORE, "", "%s" # UTM
+IFIELD IGNORE, "", "%s" # JOG
+IFIELD IGNORE, "", "%s" # FC
+IFIELD IGNORE, "", "%s" # DSG
+IFIELD IGNORE, "", "%s" # PC
+IFIELD IGNORE, "", "%s" # CC1
+IFIELD IGNORE, "", "%s" # ADM1
+IFIELD IGNORE, "", "%s" # ADM2
+IFIELD IGNORE, "", "%s" # DIM
+IFIELD IGNORE, "", "%s" # CC2
+IFIELD IGNORE, "", "%s" # NT
+IFIELD IGNORE, "", "%s" # LC
+IFIELD IGNORE, "", "%s" # SHORT_FORM
+IFIELD IGNORE, "", "%s" # GENERIC
+IFIELD SHORTNAME, "", "%s" # SORT_NAME
+IFIELD IGNORE, "", "%s" # FULL_NAME (unicoded!)
+IFIELD DESCRIPTION, "", "%s" # FULL_NAME_ND
+IFIELD IGNORE, "", "%s" # MODIFY_DATE
--- /dev/null
+# gpsbabel XCSV style file
+#
+# Format: Ozi Explorer
+# Author: Alex Mottram
+# Date: 12/09/2002
+#
+#
+# As used in ozi.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER COMMA
+RECORD_DELIMITER NEWLINE
+BADCHARS COMMA
+
+PROLOGUE OziExplorer Waypoint File Version 1.1
+PROLOGUE WGS 84
+PROLOGUE Reserved 2
+PROLOGUE Reserved 3
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD INDEX, "1", "%4d"
+IFIELD SHORTNAME, "", "%-14.14s"
+IFIELD LAT_DECIMAL, "", "%11.6lf"
+IFIELD LON_DECIMAL, "", "%11.6lf"
+IFIELD EXCEL_TIME, "", "%011.5lf"
+IFIELD CONSTANT, "0", "%3s" # icon
+IFIELD CONSTANT, "1", "%2s" # 1
+IFIELD CONSTANT, "3", "%2s" # display format opts
+IFIELD CONSTANT, "0", "%10s" # foreground color
+IFIELD CONSTANT, "65535", "%10s" # background color
+IFIELD DESCRIPTION, "", "%-40.40s"
+IFIELD CONSTANT, "0", "%2s" # pointer direction
+IFIELD CONSTANT, "0", "%2s" # garmin display flags
+IFIELD CONSTANT, "0", "%5s" # proximity distance
+IFIELD ALT_FEET, "", "%7.0lf"
+IFIELD CONSTANT, "6", "%2s" # waypt name text size
+IFIELD CONSTANT, "0", "%2s" # bold checkbox
+IFIELD CONSTANT, "17", "%2s" # symbol size
--- /dev/null
+# gpsbabel XCSV style file
+#
+# Format: MS S&T 2002/2003
+# Author: Alex Mottram
+# Date: 12/09/2002
+#
+#
+# As requested by Noel Shrum on the gpsbabel-code mailing list.
+# Name,Latitude,Longitude,Name 2,URL,Type
+# GCCBF,44.479133,-85.56515,High Rollaway by rjlint,http://www.geocaching.com/seek/cache_details.aspx?ID=3263,Traditional Cache
+# GC110D,44.6522,-85.492483,Brown Bridge Pond Peek-a-Boo Cache by Big Bird,http://www.geocaching.com/seek/cache_details.aspx?ID=4365,Traditional Cache
+# GC171C,44.70605,-85.62265,The Michigan Frog by RealDcoy & LRB,http://www.geocaching.com/seek/cache_details.aspx?ID=5916,Traditional Cache
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER COMMA
+RECORD_DELIMITER NEWLINE
+BADCHARS COMMA
+
+PROLOGUE Name,Latitude,Longitude,Name 2,URL,Type
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+# NOTE: MS S&T ONLY IMPORTS DATA, IT DOESN'T EXPORT THIS ANYWHERE SO WE CAN
+# HAVE OUR WAY WITH THE FORMATTING.
+#
+IFIELD SHORTNAME, "", "%s" # Name
+IFIELD LAT_DECIMAL, "", "%lf" # Latitude
+IFIELD LON_DECIMAL, "", "%lf" # Longitude
+IFIELD DESCRIPTION, "", "%s" # Name 2 (Big Description)
+IFIELD URL, "", "%s" # URL
+IFIELD IGNORE, "", "" # Holder for Geocache Type
--- /dev/null
+# gpsbabel XCSV style file
+#
+# Format: Tiger Data Format
+# Author: Alex Mottram
+# Date: 12/09/2002
+#
+#
+# As defined in tiger.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER COLON
+RECORD_DELIMITER NEWLINE
+BADCHARS COLON
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD LON_DECIMAL, "", "%lf"
+IFIELD LAT_DECIMAL, "", "%lf"
+IFIELD CONSTANT, "redpin", "%s"
+IFIELD DESCRIPTION, "", "%s"
--- /dev/null
+# gpsbabel XCSV style file
+#
+# Format: Delorme Xmap Conduit
+# Author: Alex Mottram
+# Date: 12/09/2002
+#
+#
+# As defined in csv.c/xmap
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER COMMASPACE
+RECORD_DELIMITER NEWLINE
+BADCHARS COMMA
+
+PROLOGUE BEGIN SYMBOL
+EPILOGUE END
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD LAT_DECIMAL, "", "%08.5lf"
+IFIELD LON_DECIMAL, "", "%08.5lf"
+IFIELD DESCRIPTION, "", "%s"
${PNAME} -i gpsdrive -f reference/gpsdrive.txt -o gpsdrive -F ${TMPDIR}/gpsdrive2.txt
diff ${TMPDIR}/gpsdrive2.txt reference/gpsdrive.txt
+# XMapHH Street Atlas USA file format
+rm -f ${TMPDIR}/xmapwpt.wpt ${TMPDIR}/xmapwpt.xmapwpt
+${PNAME} -i xmapwpt -f reference/xmapwpt.wpt -o xmapwpt -F ${TMPDIR}/xmapwpt.xmapwpt
+${PNAME} -i xmapwpt -f ${TMPDIR}/xmapwpt.xmapwpt -o xmapwpt -F ${TMPDIR}/xmapwpt.wpt
+diff ${TMPDIR}/xmapwpt.wpt reference
+
+# XCSV
+# Test that we can parse a style file, and read and write data in the
+# same xcsv format (a complete test is virtually impossible).
+echo "RECORD_DELIMITER NEWLINE" > ${TMPDIR}/testo.style
+echo "FIELD_DELIMITER COMMA" >> ${TMPDIR}/testo.style
+echo "BADCHARS COMMA" >> ${TMPDIR}/testo.style
+echo "PROLOGUE Header" >> ${TMPDIR}/testo.style
+echo "EPILOGUE Footer" >> ${TMPDIR}/testo.style
+echo "IFIELD SHORTNAME,,%s" >> ${TMPDIR}/testo.style
+echo "IFIELD LAT_DIRDECIMAL,,%c%lf" >> ${TMPDIR}/testo.style
+echo "IFIELD LON_DECIMALDIR,,%lf%c" >> ${TMPDIR}/testo.style
+rm -f ${TMPDIR}/xcsv.geo ${TMPDIR}/xcsv.xcsv
+${PNAME} -i geo -f geocaching.loc -o xcsv,style=${TMPDIR}/testo.style -F ${TMPDIR}/xcsv.geo
+${PNAME} -i xcsv,style=${TMPDIR}/testo.style -f ${TMPDIR}/xcsv.geo -o xcsv,style=${TMPDIR}/testo.style -F ${TMPDIR}/xcsv.xcsv
+diff -u ${TMPDIR}/xcsv.geo ${TMPDIR}/xcsv.xcsv
extern ff_vecs_t mxf_vecs;
extern ff_vecs_t holux_vecs;
extern ff_vecs_t ozi_vecs;
+extern ff_vecs_t xcsv_vecs;
extern ff_vecs_t tpg_vecs;
extern ff_vecs_t dna_vecs;
extern ff_vecs_t magnav_vec;
extern ff_vecs_t xmap_vecs;
+extern ff_vecs_t xmapwpt_vecs;
extern ff_vecs_t tmpro_vecs;
extern ff_vecs_t gpsdrive_vecs;
"Delorme Topo USA4/XMap Conduit",
NULL
},
+ {
+ &xmapwpt_vecs,
+ "xmapwpt",
+ "Delorme XMap HH Native .WPT",
+ ".wpt"
+ },
{
&dna_vecs,
"dna",
"OziExplorer Waypoint",
"ozi"
},
+ {
+ &xcsv_vecs,
+ "xcsv",
+ "? Character Separated Values",
+ NULL
+ },
{
&tpg_vecs,
"tpg",
--- /dev/null
+/*
+ XCSV - X Character Separated Values (.???)
+
+ A hopefully not too feeble attempt at parsing whatever separated values
+ files into the waypoint structure and back out again. This is a config-
+ file wrapper around csv_util.c.
+
+ Copyright (C) 2002 Alex Mottram (geo_alexm at cox-internet.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+
+#include <ctype.h>
+#include "defs.h"
+#include "csv_util.h"
+
+#define MYNAME "XCSV"
+#define ISSTOKEN(a,b) (strncmp(a,b, strlen(b)) == 0)
+
+/* a table of config file constants mapped to chars */
+static
+char_map_t xcsv_char_table[] = {
+ { "COMMA", "," },
+ { "COMMASPACE", ", " },
+ { "SINGLEQUOTE", "'" },
+ { "DOUBLEQUOTE", "\"" },
+ { "COLON", ":" },
+ { "SEMICOLON", ";" },
+ { "NEWLINE", "\n" },
+ { "CR", "\n" },
+ { "CRNEWLINE", "\r\n" },
+ { "TAB", "\t" },
+ { "SPACE", " " },
+ { "HASH", "#" },
+ { NULL, NULL }
+};
+
+void
+xcsv_destroy_style(void)
+{
+ queue *elem, *tmp;
+ field_map_t *fmp;
+ ogue_t *ogp;
+
+ /*
+ * If this xcsv_file struct came from a file we can free it all.
+ * If not, we can at least free the queue elements.
+ */
+
+ /* destroy the prologue */
+ QUEUE_FOR_EACH(&xcsv_file.prologue, elem, tmp) {
+ if (xcsv_file.is_internal == 0) {
+ ogp = (ogue_t *)elem;
+ if (ogp->val)
+ free(ogp->val);
+ }
+ if (elem)
+ free(elem);
+ }
+
+ /* destroy the epilogue */
+ QUEUE_FOR_EACH(&xcsv_file.epilogue, elem, tmp) {
+ if (xcsv_file.is_internal == 0) {
+ ogp = (ogue_t *)elem;
+ if (ogp->val)
+ free(ogp->val);
+ }
+ if (elem)
+ free(elem);
+ }
+
+ /* destroy the ifields */
+ QUEUE_FOR_EACH(&xcsv_file.ifield, elem, tmp) {
+ if (xcsv_file.is_internal == 0) {
+ fmp = (field_map_t *) elem;
+ if (fmp->key)
+ free(fmp->key);
+ if (fmp->val)
+ free(fmp->val);
+ if (fmp->printfc)
+ free(fmp->printfc);
+ }
+ if (elem)
+ free(elem);
+ }
+
+ /* destroy the ofields, if they are not re-mapped to ifields. */
+ if (xcsv_file.ofield != &xcsv_file.ifield) {
+ QUEUE_FOR_EACH(xcsv_file.ofield, elem, tmp) {
+ if (xcsv_file.is_internal == 0) {
+ fmp = (field_map_t *) elem;
+ if (fmp->key)
+ free(fmp->key);
+ if (fmp->val)
+ free(fmp->val);
+ if (fmp->printfc)
+ free(fmp->printfc);
+ }
+ if (elem)
+ free(elem);
+ }
+
+ if (xcsv_file.ofield)
+ free(xcsv_file.ofield);
+ }
+
+ if (xcsv_file.is_internal == 0) {
+ /* other alloc'd glory */
+ if (xcsv_file.field_delimiter)
+ free(xcsv_file.field_delimiter);
+
+ if (xcsv_file.record_delimiter)
+ free(xcsv_file.record_delimiter);
+
+ if (xcsv_file.badchars)
+ free(xcsv_file.badchars);
+ }
+
+ /* return everything to zeros */
+ memset(&xcsv_file, '\0', sizeof(xcsv_file));
+}
+
+static const char *
+get_char_from_constant_table(char *key)
+{
+ char_map_t *cm = xcsv_char_table;
+
+ while ((cm->key) && (strcmp(key, cm->key) != 0)) {
+ cm++;
+ }
+
+ return (cm->chars);
+}
+
+static void
+xcsv_read_style(const char *fname)
+{
+ char sbuff[8192];
+ int i, linecount = 0;
+ FILE *fp;
+ char *s, *p, *sp;
+ const char *cp;
+ char *key, *val, *pfc;
+
+ xcsv_file_init();
+
+ fp = fopen(fname, "r");
+
+ if (!fp)
+ fatal(MYNAME ": Cannot read style file: %s\n", fname);
+
+ do {
+ memset(sbuff, '\0', sizeof(sbuff));
+ fgets(sbuff, sizeof(sbuff), fp);
+ rtrim(sbuff);
+
+ /*
+ * tokens should be parsed longest to shortest, unless something
+ * requires a previously set value. This way something like
+ * SHORT and SHORTNAME don't collide.
+ */
+
+ /* whack off any comments */
+ if ((p = strchr(sbuff, '#')) != NULL)
+ *p = '\0';
+
+ if (strlen(sbuff)) {
+ if (ISSTOKEN(sbuff, "FIELD_DELIMITER")) {
+ sp = csv_stringtrim(&sbuff[16], "\"");
+ cp = get_char_from_constant_table(sp);
+ if (cp)
+ xcsv_file.field_delimiter = xstrdup(cp);
+ else
+ xcsv_file.field_delimiter = sp;
+ } else
+
+ if (ISSTOKEN(sbuff, "RECORD_DELIMITER")) {
+ sp = csv_stringtrim(&sbuff[17], "\"");
+ cp = get_char_from_constant_table(sp);
+ if (cp)
+ xcsv_file.record_delimiter = xstrdup(cp);
+ else
+ xcsv_file.field_delimiter = sp;
+ } else
+
+ if (ISSTOKEN(sbuff, "BADCHARS")) {
+ sp = csv_stringtrim(&sbuff[9], "\"");
+ cp = get_char_from_constant_table(sp);
+ if (cp)
+ xcsv_file.badchars = xstrdup(cp);
+ else
+ xcsv_file.badchars = sp;
+ } else
+
+ if (ISSTOKEN(sbuff, "PROLOGUE")) {
+ xcsv_prologue_add(xstrdup(&sbuff[9]));
+ } else
+
+ if (ISSTOKEN(sbuff, "EPILOGUE")) {
+ xcsv_epilogue_add(xstrdup(&sbuff[9]));
+ } else
+
+ if (ISSTOKEN(sbuff, "IFIELD")) {
+ key = val = pfc = NULL;
+
+ s = csv_lineparse(&sbuff[6], ",", "", linecount);
+
+ i = 0;
+ while (s) {
+ switch(i) {
+ case 0:
+ /* key */
+ key = csv_stringtrim(s, "\"");
+ break;
+ case 1:
+ /* default value */
+ val = csv_stringtrim(s, "\"");
+ break;
+ case 2:
+ /* printf conversion */
+ pfc = csv_stringtrim(s, "\"");
+ break;
+ default:
+ break;
+ }
+ i++;
+
+ s = csv_lineparse(NULL, ",", "", linecount);
+ }
+
+ xcsv_ifield_add(key, val, pfc);
+
+ } else
+
+ /*
+ * as OFIELDs are implemented as an after-thought, I'll
+ * leave this as it's own parsing for now. We could
+ * change the world on ifield vs ofield format later..
+ */
+ if (ISSTOKEN(sbuff, "OFIELD")) {
+ key = val = pfc = NULL;
+
+ s = csv_lineparse(&sbuff[6], ",", "", linecount);
+
+ i = 0;
+ while (s) {
+ switch(i) {
+ case 0:
+ /* key */
+ key = csv_stringtrim(s, "\"");
+ break;
+ case 1:
+ /* default value */
+ val = csv_stringtrim(s, "\"");
+ break;
+ case 2:
+ /* printf conversion */
+ pfc = csv_stringtrim(s, "\"");
+ break;
+ default:
+ break;
+ }
+ i++;
+ s = csv_lineparse(NULL, ",", "", linecount);
+ }
+
+ xcsv_ofield_add(key, val, pfc);
+ }
+ }
+ } while (!feof(fp));
+
+ /* if we have no output fields, use input fields as output fields */
+ if (xcsv_file.ofield_ct == 0) {
+ if (xcsv_file.ofield)
+ free(xcsv_file.ofield);
+ xcsv_file.ofield = &xcsv_file.ifield;
+ xcsv_file.ofield_ct = xcsv_file.ifield_ct;
+ }
+
+ fclose(fp);
+}
+
+static void
+xcsv_rd_init(const char *fname, const char *args)
+{
+ const char *p;
+
+ /*
+ * if we don't have an internal style defined, we need to
+ * read it from a user-supplied style file, or die trying.
+ */
+ if (xcsv_file.is_internal == 0) {
+ p = get_option(args, "style");
+
+ if (!p)
+ fatal(MYNAME ": XCSV input style not declared. Use ... -i xcsv,style=path/to/file.style\n");
+
+ xcsv_read_style(p);
+ }
+
+ xcsv_file.xcsvfp = fopen(fname, "r");
+
+ if (xcsv_file.xcsvfp == NULL)
+ fatal(MYNAME ": Cannot open %s for reading\n", fname);
+}
+
+static void
+xcsv_rd_deinit(void)
+{
+ fclose(xcsv_file.xcsvfp);
+
+ xcsv_destroy_style();
+}
+
+static void
+xcsv_wr_init(const char *fname, const char *args)
+{
+ const char * p;
+
+ /* if we don't have an internal style defined, we need to
+ * read it from a user-supplied style file, or die trying.
+ */
+ if (xcsv_file.is_internal == 0) {
+ p = get_option(args, "style");
+
+ if (!p)
+ fatal(MYNAME ": XCSV output style not declared. Use ... -o xcsv,style=path/to/file.style\n");
+
+ xcsv_read_style(p);
+
+ /* set mkshort options from the command line */
+ if (global_opts.synthesize_shortnames) {
+ p = get_option(args, "snlen");
+ if (p)
+ setshort_length(atoi(p));
+
+ p = get_option(args, "snwhite");
+ if (p)
+ setshort_whitespace_ok(atoi(p));
+
+ p = get_option(args, "snupper");
+ if (p)
+ setshort_mustupper(atoi(p));
+
+ setshort_badchars(xcsv_file.badchars);
+ }
+ }
+
+ xcsv_file.xcsvfp = fopen(fname, "w");
+
+ if (xcsv_file.xcsvfp == NULL)
+ fatal(MYNAME ": Cannot open %s for writing\n", fname);
+}
+
+static void
+xcsv_wr_deinit(void)
+{
+ fclose(xcsv_file.xcsvfp);
+
+ xcsv_destroy_style();
+}
+
+ff_vecs_t xcsv_vecs = {
+ xcsv_rd_init,
+ xcsv_wr_init,
+ xcsv_rd_deinit,
+ xcsv_wr_deinit,
+ xcsv_data_read,
+ xcsv_data_write,
+};
--- /dev/null
+/*
+ Delorme XMap HandHeld .WPT Format
+ (as created by XMapHH Street Atlas/PPC)
+ 1296126539:1481466224:1895825408:1392508928:3137157:text:text:text\n
+
+ Contributed to gpsbabel by Alex Mottram (geo_alexm at cox-internet.com)
+
+ Copyright (C) 2002 Robert Lipe, robertlipe@usa.net
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+
+#include "defs.h"
+#include "csv_util.h"
+
+#define MYNAME "XMAPWPT"
+
+static void
+xmapwpt_set_style()
+{
+ /* set up the xmapwpt xcsv_file struct */
+ xcsv_file_init();
+
+ /* this is an internal style, don't mess with it */
+ xcsv_file.is_internal = 1;
+
+ /* how the file gets split up */
+ xcsv_file.field_delimiter = ":";
+ xcsv_file.record_delimiter = "\n";
+ xcsv_file.badchars = ":";
+
+ xcsv_ifield_add("CONSTANT", "1296126539", "%s");
+ xcsv_ifield_add("CONSTANT", "1481466224", "%s");
+ xcsv_ifield_add("LAT_INT32DEG", "", "%d");
+ xcsv_ifield_add("LON_INT32DEG", "", "%d");
+ xcsv_ifield_add("CONSTANT", "3137157", "%s");
+ xcsv_ifield_add("SHORTNAME", "", "%-.31s");
+ xcsv_ifield_add("IGNORE", "", "%-.31s");
+
+ /*
+ * actual description len accepted is 79. however under win32, we
+ * run the risk of the compiled app ending a line in \r\n when we
+ * say \n. This, in turn, overruns a fixed len buffer and causes
+ * XmapHH to die both occasionally and horribly.
+ */
+ xcsv_ifield_add("DESCRIPTION", "", "%-.78s");
+
+ /* outfields are infields */
+ if (xcsv_file.ofield)
+ free(xcsv_file.ofield);
+ xcsv_file.ofield = &xcsv_file.ifield;
+ xcsv_file.ofield_ct = xcsv_file.ifield_ct;
+
+ /* set up mkshort */
+ if (global_opts.synthesize_shortnames) {
+ setshort_length(32);
+ setshort_whitespace_ok(0);
+ setshort_badchars(xcsv_file.badchars);
+ }
+}
+
+static void
+xmapwpt_rd_init(const char *fname, const char *args)
+{
+ xmapwpt_set_style();
+
+ xcsv_file.xcsvfp = fopen(fname, "r");
+
+ if (xcsv_file.xcsvfp == NULL) {
+ fatal(MYNAME ": Cannot open %s for reading\n", fname );
+ }
+}
+
+static void
+xmapwpt_wr_init(const char *fname, const char *args)
+{
+ xmapwpt_set_style();
+
+ xcsv_file.xcsvfp = fopen(fname, "w");
+
+ if (xcsv_file.xcsvfp == NULL) {
+ fatal(MYNAME ": Cannot open %s for reading\n", fname );
+ }
+}
+
+static void
+xmapwpt_deinit(void)
+{
+ if (xcsv_file.xcsvfp)
+ fclose(xcsv_file.xcsvfp);
+
+ xcsv_destroy_style();
+}
+
+ff_vecs_t xmapwpt_vecs = {
+ xmapwpt_rd_init,
+ xmapwpt_wr_init,
+ xmapwpt_deinit,
+ xmapwpt_deinit,
+ xcsv_data_read,
+ xcsv_data_write,
+};